package cn.anecansaitin.whimsy.common.capabilities.battle;

import cn.anecansaitin.whimsy.client.registers.ModSounds;
import cn.anecansaitin.whimsy.common.entity.LightBullet;
import cn.anecansaitin.whimsy.common.entity.ow.SeismicSlamWave;
import cn.anecansaitin.whimsy.common.extension.obb.OBB;
import cn.anecansaitin.whimsy.common.network.KeyInputPack;
import cn.anecansaitin.whimsy.common.network.NetworkHelper;
import cn.anecansaitin.whimsy.common.register.ModDamageSources;
import cn.anecansaitin.whimsy.common.register.ModEffects;
import cn.anecansaitin.whimsy.util.AABBUtil;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import com.mojang.blaze3d.platform.InputConstants;
import it.unimi.dsi.fastutil.floats.FloatConsumer;
import net.minecraft.client.player.Input;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.nbt.*;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.util.Mth;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.attributes.Attribute;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import net.neoforged.neoforge.common.util.INBTSerializable;
import org.jetbrains.annotations.Nullable;
import org.joml.Math;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import org.lwjgl.glfw.GLFW;

import java.util.HashSet;
import java.util.List;
import java.util.UUID;
import java.util.function.IntConsumer;

/**
 * 末日铁拳技能组
 */
public class DoomfistBattle implements IOWController {
    private final byte[] keyResult = new byte[2];
    public final int tickScaling = 1000;
    public final RocketPunch rocketPunch;
    public final SeismicSlam seismicSlam;
    public final PowerBlock powerBlock = new PowerBlock(tickScaling);
    public final MeteorStrike meteorStrike = new MeteorStrike(tickScaling);
    public final UltimateEfficiency ultimateEfficiency = new UltimateEfficiency(tickScaling);
    public final AuxiliaryAttack auxiliaryAttack = new AuxiliaryAttack(tickScaling);
    public final HandCannon handCannon;
    public final DoomfistAnim doomfistAnim = new DoomfistAnim();

    public final ExpandedHP expandedHP = new ExpandedHP(tickScaling);

    public final IntConsumer generateShield = ultimateEfficiency::generate;
    public final FloatConsumer ultimateCharger = damage -> meteorStrike.charge(damage, tickScaling);

    public DoomfistBattle() {
        expandedHP.addTemporary(UUID.randomUUID(), ultimateEfficiency.temporaryHP);
        rocketPunch = new RocketPunch(tickScaling, generateShield, ultimateCharger);
        seismicSlam = new SeismicSlam(tickScaling, generateShield, ultimateCharger);
        handCannon = new HandCannon(tickScaling, ultimateCharger);
    }

    @Override
    public void tick(LivingEntity livingEntity) {
        cdTick(livingEntity);
        skillTick(livingEntity);
    }

    @Override
    public float hurt(LivingEntity entity, DamageSource source, float amount) {
        //悍猛格挡判定
        if (powerBlock.isCasting) {
            Vec3 sourcePos = null;

            if (source.getDirectEntity() != null) {
                sourcePos = source.getDirectEntity().position();
            } else if (source.getEntity() != null) {
                sourcePos = source.getEntity().position();
            } else if (source.getSourcePosition() != null) {
                sourcePos = source.getSourcePosition();
            }

            //无来源的伤害与来源与正面地伤害可以减免
            if (sourcePos == null || checkDamageDirection(sourcePos, entity)) {
                float block = amount * 0.8f;
                amount = amount - block;

                //吸收能量
                if (powerBlock.addDamage(block)) {
                    rocketPunch.resetting(entity);
                    rocketPunch.setEnhanced();
                }
            }
        }

        return IOWController.super.hurt(entity, source, amount);
    }

    /**
     * 判断来源伤害方向，为正面时可进行伤害减免
     */
    private boolean checkDamageDirection(Vec3 pos, LivingEntity entity) {
        Vector3f entityPos = entity.position().toVector3f();
        Vector3f enemyPos = pos.toVector3f();
        Vector3f enemyTOEntity = entityPos.sub(enemyPos);
        Vector3f view = entity.getViewVector(1).toVector3f();
        float dot = enemyTOEntity.dot(view);
        float cos = dot / (enemyTOEntity.length() * view.length());
        return Math.toDegrees(Math.acos(cos)) < 180;
    }

    /**
     * 更新各个技能的CD
     */
    private void cdTick(LivingEntity livingEntity) {
        //如果毁天灭地处于释放阶段，则末日铁拳技能组CD为2倍速率恢复
        if (meteorStrike.isCasting) {
            rocketPunch.cdTick(tickScaling * 2);
            seismicSlam.cdTick(tickScaling * 2);
            powerBlock.cdTick(tickScaling * 2);
        } else {
            rocketPunch.cdTick(tickScaling);
            seismicSlam.cdTick(tickScaling);
            powerBlock.cdTick(tickScaling);
            //只有毁天灭地未处于释放状态，才能恢复CD
            meteorStrike.cdTick(tickScaling);
        }

        //最佳防守的速率不受毁天灭地影响
        ultimateEfficiency.cdTick(tickScaling);
        //辅助攻击的速率不受毁天灭地影响
        auxiliaryAttack.cdTick(tickScaling);
        handCannon.cdTick(livingEntity, tickScaling);

        expandedHP.tick(tickScaling);
    }

    /**
     * 更新各个技能的效果
     */
    private void skillTick(LivingEntity livingEntity) {
        rocketPunch.skillTick(livingEntity, tickScaling);
        seismicSlam.skillTick(livingEntity, tickScaling);
        powerBlock.skillTick(livingEntity, tickScaling);
        meteorStrike.skillTick(livingEntity, tickScaling);
    }

    @Override
    public byte[] keyInput(LivingEntity livingEntity, int key, int action, int modifiers) {
        switch (key) {
            //右键蓄力
            case GLFW.GLFW_MOUSE_BUTTON_RIGHT -> {
                switch (action) {
                    //右键释放
                    case InputConstants.RELEASE -> {
                        if (rocketPunch.isCharging) {
                            rocketPunch.chargeComplete(livingEntity);
                            return setResult(true, true);
                        }
                    }
                    //右键按下
                    case InputConstants.PRESS -> {
                        if (meteorStrike.isCasting ||seismicSlam.isCasting) {
                            //大招与裂地重拳期间无法释放
                            return setResult(false, true);
                        }

                        if (rocketPunch.tryUse(livingEntity)) {
                            if (powerBlock.isCasting) {
                                //先判断是否在格挡，取消格挡
                                powerBlock.castingCompleted(livingEntity);
                            }

                            return setResult(true, true);
                        }
                    }
                }
            }
            //E裂地猛击
            case GLFW.GLFW_KEY_E -> {
                if (action != InputConstants.PRESS) {
                    return setResult(false, true);
                }

                //如果正在裂地猛击，则取消裂地猛击
                if (seismicSlam.isCasting) {
                    seismicSlam.castingCompleted();
                    return setResult(true, true);
                } else if (meteorStrike.isCasting || rocketPunch.isCasting) {
                    //大招期间无法使用
                    return setResult(false, true);
                } else if (seismicSlam.tryUse(livingEntity)) {
                    //先判断是否在格挡，取消格挡
                    if (powerBlock.isCasting) {
                        powerBlock.castingCompleted(livingEntity);
                    }

                    return setResult(true, true);
                }

                return setResult(false, true);
            }
            //空格超级跳
            case GLFW.GLFW_KEY_SPACE -> {
                if (action != InputConstants.PRESS) {
                    return setResult(false, false);
                }

                //在大招期间，判定是否处于前后摇
                if (meteorStrike.cantJump()) {
                    return setResult(false, true);
                }

                //如果冲刺时间大于0，表示正在冲刺，则取消冲刺
                if (rocketPunch.duration > 0) {
                    rocketPunch.superJump(livingEntity);
                    return setResult(true, true);
                }
            }
            //左Shift悍将格挡
            case GLFW.GLFW_KEY_LEFT_SHIFT -> {
                if (action != InputConstants.PRESS) {
                    return setResult(false, true);
                }

                if (powerBlock.isCasting) {
                    //可以取消格挡
                    powerBlock.castingCompleted(livingEntity);
                    return setResult(true, true);
                } else if (meteorStrike.isCasting || rocketPunch.isCasting) {
                    //大招与火箭重拳期间无法使用
                    return setResult(false, true);
                } else if (powerBlock.tryUse(livingEntity)) {
                    if (seismicSlam.isCasting) {
                        //可以取消裂地重拳
                        seismicSlam.castingCompleted();
                    }

                    return setResult(true, true);
                }
            }
            //左键停止蓄力/停止格挡/手炮射击
            case GLFW.GLFW_MOUSE_BUTTON_LEFT -> {
                if (action != InputConstants.PRESS) {
                    return setResult(false, true);
                }

                //提前确认大招
                if (meteorStrike.confirm()) {
                    return setResult(true, true);
                }

                if (rocketPunch.isCharging) {
                    rocketPunch.interrupt(livingEntity);
                    return setResult(true, true);
                } else if (powerBlock.isCasting) {
                    powerBlock.castingCompleted(livingEntity);
                    return setResult(true, true);
                } else if (auxiliaryAttack.canMainAttack() && !rocketPunch.isCasting && !seismicSlam.isCasting && !meteorStrike.isCasting) {
                    handCannon.shoot(livingEntity);
                    return setResult(true, true);
                }

                return setResult(false, true);
            }
            //鼠标侧键辅助攻击
            case GLFW.GLFW_MOUSE_BUTTON_5, GLFW.GLFW_KEY_V -> {
                //有任何技能正在释放，就不要触发辅助攻击
                if (action != InputConstants.PRESS || seismicSlam.isCasting || rocketPunch.isCasting || powerBlock.isCasting || meteorStrike.isCasting) {
                    return setResult(false, true);
                }

                return setResult(auxiliaryAttack.attack(livingEntity), true);
            }

            //毁天灭地
            case GLFW.GLFW_KEY_Q -> {
                if (action != InputConstants.PRESS) {
                    return setResult(false, true);
                }

                //提前确认大招
                if (meteorStrike.confirm()) {
                    return setResult(true, true);
                }

                //尝试释放大招
                return setResult(meteorStrike.tryUse(livingEntity), true);
            }
        }

        return setResult(false, false);
    }

    private byte[] setResult(boolean sendToServer, boolean cancel) {
        keyResult[0] = (byte) (sendToServer ? 1 : 0);
        keyResult[1] = (byte) (cancel ? 1 : 0);
        return keyResult;
    }

    @Override
    public void sendKeyToServer(LivingEntity livingEntity, int key, int action, int modifiers) {
        NetworkHelper.toServerPlayer(new KeyInputPack(KeyInputPack.DOOMFIST, new int[]{key, action, modifiers}));
    }

    @Override
    public IAnimController getAnimatable() {
        return doomfistAnim;
    }

    @Override
    public CompoundTag serializeNBT() {
        CompoundTag tag = new CompoundTag();
        //火箭重拳
        tag.put("punch", rocketPunch.serializeNBT());
        //裂地重拳
        tag.put("seismic_slam", seismicSlam.serializeNBT());
        //悍将格挡
        tag.put("power_block", powerBlock.serializeNBT());
        //毁天灭地
        tag.put("meteor_strike", meteorStrike.serializeNBT());
        //最佳防守
        tag.put("ultimate_efficiency", ultimateEfficiency.serializeNBT());
        return tag;
    }

    @Override
    public void deserializeNBT(CompoundTag nbt) {
        //火箭重拳
        rocketPunch.deserializeNBT(nbt.getCompound("punch"));
        //裂地重拳
        seismicSlam.deserializeNBT(nbt.getCompound("seismic_slam"));
        //悍将格挡
        powerBlock.deserializeNBT(nbt.getCompound("power_block"));
        //毁天灭地
        meteorStrike.deserializeNBT(nbt.getCompound("meteor_strike"));
        //最佳防守
        ultimateEfficiency.deserializeNBT(nbt.getCompound("ultimate_efficiency"));
    }

    @Override
    public String getId() {
        return "doomfist";
    }

    @Override
    public void onQuitedMode(LivingEntity entity) {
        rocketPunch.onQuitedMode(entity);
        powerBlock.onQuitedMode(entity);
    }

    @Override
    public ExpandedHP expandedHP() {
        return expandedHP;
    }

    public static final class RocketPunch implements INBTSerializable<CompoundTag> {
        private final IntConsumer generateShield;
        private final FloatConsumer ultimateCharger;
        /**
         * 默认总CD
         */
        private final int defaultTick;
        /**
         * 默认最大蓄力时间
         */
        private final int maxRechargeTick;
        /**
         * 默认最大右击时间
         */
        private final int maxChargingTick;
        /**
         * 冷却CD
         */
        private int cd;
        /**
         * 蓄力时间
         */
        private int rechargeTick;
        /**
         * 右击总时间
         */
        private int chargingTick;
        /**
         * 是否为强化拳
         */
        private boolean isEnhanced;
        /**
         * 是否正在蓄力
         */
        private boolean isCharging;
        /**
         * 技能是否正在释放中
         */
        private boolean isCasting;
        /**
         * 技能释放时的冲刺方向，仅XZ方向
         */
        private Vec3 direction;
        /**
         * 冲刺的持续时间
         */
        private int duration;
        /**
         * 正在蓄力时的属性操作，降低移动速度
         */
        private static final Multimap<Attribute, AttributeModifier> CHARGING_MODIFIER = ImmutableMultimap.of(Attributes.MOVEMENT_SPEED, new AttributeModifier(UUID.fromString("A4D08A13-B526-6BF3-395B-0CBB496D4D0D"), "rocket_punch_charging", -0.5, AttributeModifier.Operation.MULTIPLY_TOTAL));

        private RocketPunch(int cdScaling, IntConsumer generateShield, FloatConsumer ultimateCharger) {
            this.generateShield = generateShield;
            this.ultimateCharger = ultimateCharger;
            defaultTick = 4 * 20 * cdScaling;
            maxRechargeTick = 20 * cdScaling;
            maxChargingTick = 32 * cdScaling;
        }

        private void cdTick(int tick) {
            if (!isCasting && cd > 0) {
                cd -= tick;
            }
        }

        private void skillTick(LivingEntity entity, int tick) {
            if (!isCasting) {
                return;
            }

            if (isCharging) {
                playRocketPunchRechargeSound(chargingTick / tick, entity);
                if (rechargeTick < maxRechargeTick) {
                    rechargeTick += tick;
                }

                if (chargingTick < maxChargingTick) {
                    chargingTick += tick;
                }
                //如果蓄力时间大于最大蓄力时间，则结束蓄力
                if (chargingTick >= maxChargingTick) {
                    chargeComplete(entity);
                }
            } else {
                if (duration > 0) {
                    //判断是否撞到敌人
                    if (attack(entity)) {
                        castingCompleted(entity);
                        return;
                    }

                    Vec3 velocity = entity.getDeltaMovement();
                    //如果速度小于一定值就停止技能，相当于撞墙了
                    if (Mth.sqrt((float) ((velocity.x * velocity.x) + (velocity.z * velocity.z))) < 0.5f) {
                        castingCompleted(entity);
                    } else {
                        entity.setDeltaMovement(direction);
                        duration -= tick;
                    }
                } else {
                    castingCompleted(entity);
                }
            }
        }

        private boolean attack(LivingEntity livingEntity) {
            for (Entity entity : livingEntity.level().getEntities(livingEntity, livingEntity.getBoundingBox().expandTowards(livingEntity.getDeltaMovement()).inflate(1.0), entity -> entity instanceof LivingEntity)) {
                if (AABBUtil.isMovingEntityCollisionWithEntity(livingEntity, entity, 0.8)) {
                    Vec3 vec3 = Vec3.directionFromRotation(0, livingEntity.getYRot());

                    if (isEnhanced) {
                        vec3 = vec3.scale(2.5f * 1.5f);
                    } else {
                        vec3 = vec3.scale(2.5f);
                    }

                    float enhanced = isEnhanced ? 1.5f : 1f;
                    float rechargeRate = (float) rechargeTick / maxRechargeTick;
                    //直接造成的伤害
                    float damage1 = 7.5f * rechargeRate * enhanced;
                    //撞墙后造成的伤害
                    float damage2 = rechargeRate * 4f * enhanced;
                    int duration = (int) (5.2f * rechargeRate);

                    //获取周围的实体
                    List<Entity> entities = entity.level().getEntities(livingEntity, entity.getBoundingBox().inflate(1.5));
                    //给大招充能
                    ultimateCharger.accept(damage1 * entities.size());

                    for (Entity enemy : entities) {
                        enemy.hurt(ModDamageSources.owNoKnockback(livingEntity.level(), livingEntity, null, livingEntity.position()), damage1);
                        CompoundTag tag = enemy.getPersistentData();
                        CompoundTag rockData;

                        if (tag.contains("rock_punch", CompoundTag.TAG_COMPOUND)) {
                            rockData = tag.getCompound("rock_punch");
                        } else {
                            rockData = new CompoundTag();
                            tag.put("rock_punch", rockData);
                        }

                        rockData.putFloat("x", (float) vec3.x);
                        rockData.putFloat("z", (float) vec3.z);
                        rockData.putFloat("d", damage2);

                        if (enemy instanceof LivingEntity livingEnemy) {
                            livingEnemy.addEffect(new MobEffectInstance(ModEffects.IMPACT.get(), duration));
                        }

                        enemy.setDeltaMovement(vec3.x, 0, vec3.z);
                    }

                    //生成临时生命
                    generateShield.accept(entities.size());
                    return true;
                }
            }

            return false;
        }

        public boolean tryUse(LivingEntity livingEntity) {
            if (cd <= 0 && !isCharging && !isCasting) {
                cd = defaultTick;
                isCharging = true;
                isCasting = true;
                //添加减速属性
                livingEntity.getAttributes().addTransientAttributeModifiers(CHARGING_MODIFIER);
                return true;
            } else {
                return false;
            }
        }

        /**
         * 结束蓄力状态
         */
        public void chargeComplete(LivingEntity livingEntity) {
            chargingTick = 0;
            //冲刺持续时间
            duration = rechargeTick * 11 / 20;
            isEnhanced = false;
            isCharging = false;
            //移除减速属性
            livingEntity.getAttributes().removeAttributeModifiers(CHARGING_MODIFIER);
            //获取冲刺向量，根据是否充能来决定速度
            direction = Vec3.directionFromRotation(0, livingEntity.getYRot()).scale(isEnhanced ? 1.8 : 1.2);
            livingEntity.setDeltaMovement(direction);
        }

        /**
         * 技能完全结束
         */
        public void castingCompleted(LivingEntity entity) {
            rechargeTick = 0;
            isCasting = false;
            direction = null;
            //清空持续时间
            duration = 0;
            entity.setDeltaMovement(Vec3.ZERO);
        }

        /**
         * 中断
         */
        public void interrupt(LivingEntity livingEntity) {
            rechargeTick = 0;
            chargingTick = 0;
            duration = 0;
            isCharging = false;
            isCasting = false;
            livingEntity.getAttributes().removeAttributeModifiers(CHARGING_MODIFIER);
            direction = null;
        }

        /**
         * 重置
         */
        public void resetting(LivingEntity livingEntity) {
            cd = 0;
            rechargeTick = 0;
            chargingTick = 0;
            duration = 0;
            isEnhanced = false;
            isCharging = false;
            isCasting = false;
            livingEntity.getAttributes().removeAttributeModifiers(CHARGING_MODIFIER);
            direction = null;
        }

        /**
         * 超级跳
         */
        private void superJump(LivingEntity livingEntity) {
            castingCompleted(livingEntity);
            Vec3 deltaMovement = Vec3.directionFromRotation(0, livingEntity.getYRot()).scale(2).add(0, 0.5, 0);
            livingEntity.setDeltaMovement(deltaMovement);
        }

        public void setEnhanced() {
            isEnhanced = true;
        }

        public boolean isChargingComplete() {
            return chargingTick >= maxChargingTick;
        }

        public boolean isInCharging() {
            return chargingTick > 0 && chargingTick < maxChargingTick;
        }

        private void onQuitedMode(LivingEntity entity) {
            entity.getAttributes().removeAttributeModifiers(CHARGING_MODIFIER);
            direction = null;
        }

        /**
         * 获取蓄力时的音效<br>
         * 因为原版的音效播放没法方便得停止，我将长音效切割为短音效并按需播放。
         *
         * @param chargingTick 蓄力的时间
         * @param livingEntity 正在蓄力的实体
         */
        private void playRocketPunchRechargeSound(int chargingTick, LivingEntity livingEntity) {
            SoundEvent sound = null;

            switch (chargingTick) {
                case 0 -> sound = ModSounds.ROCKET_PUNCH_RECHARGE_1.get();
                case 8 -> sound = ModSounds.ROCKET_PUNCH_RECHARGE_2.get();
                case 16 -> sound = ModSounds.ROCKET_PUNCH_RECHARGE_3.get();
                case 24 -> sound = ModSounds.ROCKET_PUNCH_RECHARGE_4.get();
                case 32 -> sound = ModSounds.ROCKET_PUNCH_RECHARGE_5.get();
                case 40 -> sound = ModSounds.ROCKET_PUNCH_RECHARGE_6.get();
                case 48 -> sound = ModSounds.ROCKET_PUNCH_RECHARGE_7.get();
                default -> {
                    return;
                }
            }

            livingEntity.level().playSound(null, livingEntity.getX(), livingEntity.getY(), livingEntity.getZ(), sound, livingEntity.getSoundSource());
        }

        @Override
        public CompoundTag serializeNBT() {
            CompoundTag tag = new CompoundTag();
            tag.putInt("cd", cd);
            tag.putInt("recharge_tick", rechargeTick);
            tag.putInt("charging_tick", chargingTick);
            tag.putBoolean("is_enhanced", isEnhanced);
            tag.putBoolean("is_charging", isCharging);
            tag.putBoolean("is_casting", isCasting);
            tag.putInt("duration", duration);

            if (direction != null) {
                ListTag directionTag = new ListTag();
                tag.put("direction", directionTag);
                directionTag.add(DoubleTag.valueOf(direction.x));
                directionTag.add(DoubleTag.valueOf(direction.z));
            }

            return tag;
        }

        @Override
        public void deserializeNBT(CompoundTag nbt) {
            cd = nbt.getInt("cd");
            rechargeTick = nbt.getInt("recharge_tick");
            chargingTick = nbt.getInt("charging_tick");
            isEnhanced = nbt.getBoolean("is_enhanced");
            isCharging = nbt.getBoolean("is_charging");
            isCasting = nbt.getBoolean("is_casting");
            duration = nbt.getInt("duration");

            if (!nbt.contains("direction")) {
                ListTag directionTag = nbt.getList("direction", 6);
                direction = new Vec3(directionTag.getDouble(0), 0, directionTag.getDouble(1));
            } else {
                direction = null;
            }
        }

        public boolean isCasting() {
            return isCasting;
        }

        public int cd() {
            return cd;
        }

        public int defaultTick() {
            return defaultTick;
        }

        public boolean isCharging() {
            return isCharging;
        }

        public int rechargeTick() {
            return rechargeTick;
        }

        public int maxRechargeTick() {
            return maxRechargeTick;
        }
    }

    public static final class SeismicSlam implements INBTSerializable<CompoundTag> {
        private final IntConsumer generatingShield;
        private final FloatConsumer ultimateCharger;
        private final int defaultCd;
        private int cd;
        private boolean isCasting;
        /**
         * 存放x轴临时速度，用于在技能期间保持高速
         */
        private float xSpeed;
        /**
         * 存放z轴临时速度，用于在技能期间保持高速
         */
        private float zSpeed;

        private SeismicSlam(int cdScaling, IntConsumer generatingShield, FloatConsumer ultimateCharger) {
            defaultCd = 1 * 20 * cdScaling;
            this.generatingShield = generatingShield;
            this.ultimateCharger = ultimateCharger;
        }

        private void cdTick(int cd) {
            if (this.cd > 0 && !isCasting) {
                this.cd -= cd;
            }
        }

        private void skillTick(LivingEntity entity, int tick) {
            if (!isCasting) {
                return;
            }

            //不在地面，进行速度的计算
            if (!entity.onGround()) {
                //为最大值，表示不加速
                if (xSpeed != Float.MAX_VALUE) {
                    Vector3f movement = entity.getDeltaMovement().toVector3f();
                    Vector3f noY = new Vector3f(movement.x, 0, movement.z);

                    //上一tick的移动向量
                    Vector3f priorMovement = new Vector3f(xSpeed, 0, zSpeed);
                    //当角度变化大于10°，不进行加速
                    if (noY.angle(priorMovement) <= 0.174533f) {
                        //将速度变为之前缓存的速度，但方向不变
                        Vector3f f = noY.normalize().mul(priorMovement.length()).add(0, movement.y, 0);
                        entity.setDeltaMovement(new Vec3(f));
                        Vec3 deltaMovement = entity.getDeltaMovement();
                        xSpeed = (float) deltaMovement.x;
                        zSpeed = (float) deltaMovement.z;
                    } else {
                        //设置为最大值，代表不加速
                        xSpeed = Float.MAX_VALUE;
                    }
                }

                //根据方向键来进行空中移动，增加灵活性
                if (entity instanceof LocalPlayer player) {
                    Input input = player.input;

                    //仅当存在输入，且有实际上的移动方向时触发
                    if (input.up ^ input.down || input.left ^ input.right) {
                        Vector3f v = new Vector3f((input.left ? 1 : 0) + (input.right ? -1 : 0), 0, (input.up ? 1 : 0) + (input.down ? -1 : 0));
                        v.rotateY(-(float) Math.toRadians(player.getYRot()));
                        //这是空中变向的速度
                        v.normalize(0.05f);
                        Vec3 movement = player.getDeltaMovement();
                        player.setDeltaMovement(movement.x() + v.x, movement.y(), movement.z() + v.z);
                    }
                }

                return;
            }

            //执行到这里，说明实体落地，需要产生攻击波

            if (entity.level().isClientSide) {
                castingCompleted();
                xSpeed = Float.MAX_VALUE;
                return;
            }

            //触发伤害，发射波
            HashSet<Entity> entities = new HashSet<>();
            float yRot = entity.getYRot() - 7.5f * 4;

            for (int i = 0; i < 9; i++) {
                entity.level().addFreshEntity(new SeismicSlamWave(yRot, entity.getX(), entity.getY(), entity.getZ(), entities, entity, generatingShield, ultimateCharger));
                yRot += 7.5f;
            }

            castingCompleted();
            xSpeed = Float.MAX_VALUE;
        }

        public boolean tryUse(LivingEntity livingEntity) {
            if (cd <= 0 && !isCasting) {
                cd = defaultCd;
                isCasting = true;
                //起跳并设置为离地
                if (livingEntity.onGround()) {
                    livingEntity.setOnGround(false);
                    livingEntity.setPos(livingEntity.position().add(0, 0.1, 0));
                }

                //根据视角方向加速
                float xRot = livingEntity.getXRot();

                if (xRot > -20) {
                    xRot = -20;
                } else if (xRot < -80) {
                    xRot = -75;
                }

                float f = xRot * (float) (Math.PI / 180.0);
                float f1 = -livingEntity.getYRot() * (float) (Math.PI / 180.0);
                float f2 = Mth.cos(f1);
                float f3 = Mth.sin(f1);
                float f4 = Mth.cos(f);
                float f5 = Mth.sin(f);
                xSpeed = f3 * f4;
                zSpeed = f2 * f4;
                //发射速度
                Vec3 deltaMovement = new Vec3(xSpeed, -f5, zSpeed).scale(1.5f);

                livingEntity.setDeltaMovement(deltaMovement);
                return true;
            } else {
                return false;
            }
        }

        public void castingCompleted() {
            isCasting = false;
        }

        @Override
        public CompoundTag serializeNBT() {
            CompoundTag tag = new CompoundTag();
            tag.putInt("cd", cd);
            tag.putBoolean("is_casting", isCasting);
            return tag;
        }

        @Override
        public void deserializeNBT(CompoundTag nbt) {
            cd = nbt.getInt("cd");
            isCasting = nbt.getBoolean("is_casting");
        }

        public boolean isCasting() {
            return isCasting;
        }

        public int cd() {
            return cd;
        }

        public int defaultCd() {
            return defaultCd;
        }
    }

    public static final class PowerBlock implements INBTSerializable<CompoundTag> {
        private final int maxCastingTick;
        private final int maxDamage = 10;
        private final int defaultCd;
        private int cd;
        private int castingTick;
        private boolean isCasting;
        private float blockedDamage;
        private static final Multimap<Attribute, AttributeModifier> BLOCK_MODIFIER = ImmutableMultimap.of(Attributes.MOVEMENT_SPEED, new AttributeModifier(UUID.fromString("ACD83C7A-0E5A-2B87-78A8-9E3C93C1F55B"), "power_block_casting", -0.35, AttributeModifier.Operation.MULTIPLY_TOTAL));


        private PowerBlock(int cdScaling) {
            maxCastingTick = 2 * 20 * cdScaling;
            defaultCd = 7 * 20 * cdScaling;
        }

        private void cdTick(int cd) {
            if (this.cd > 0 && !isCasting) {
                this.cd -= cd;
            }
        }

        private void skillTick(LivingEntity entity, int tick) {
            if (!isCasting) {
                return;
            }

            if (castingTick < maxCastingTick) {
                castingTick += tick;
            } else {
                castingCompleted(entity);
            }
        }

        private boolean tryUse(LivingEntity entity) {
            if (cd <= 0 && !isCasting) {
                cd = defaultCd;
                isCasting = true;
                entity.getAttributes().addTransientAttributeModifiers(BLOCK_MODIFIER);
                return true;
            } else {
                return false;
            }
        }

        private void castingCompleted(LivingEntity entity) {
            isCasting = false;
            blockedDamage = 0;
            castingTick = 0;
            entity.getAttributes().removeAttributeModifiers(BLOCK_MODIFIER);
        }

        /**
         * @return 本次吸收能量是否达到充能阈值
         */
        private boolean addDamage(float damage) {
            if (isCasting && blockedDamage < maxDamage) {
                blockedDamage = Math.min(maxDamage, damage + blockedDamage);

                return blockedDamage >= maxDamage;
            }

            return isCasting && blockedDamage >= maxDamage;
        }

        /**
         * 清理格挡Effect
         *
         * @param entity 退出模式时触发
         */
        private void onQuitedMode(LivingEntity entity) {
            castingTick = 0;
            castingCompleted(entity);
        }

        @Override
        public CompoundTag serializeNBT() {
            CompoundTag tag = new CompoundTag();
            tag.putInt("cd", cd);
            tag.putBoolean("is_casting", isCasting);
            tag.putFloat("blocked_damage", blockedDamage);
            return tag;
        }

        @Override
        public void deserializeNBT(CompoundTag nbt) {
            cd = nbt.getInt("cd");
            isCasting = nbt.getBoolean("is_casting");
            blockedDamage = nbt.getFloat("blocked_damage");
        }

        public boolean isCasting() {
            return isCasting;
        }

        public int cd() {
            return cd;
        }

        public int defaultCd() {
            return defaultCd;
        }

        public float blockedDamage() {
            return blockedDamage;
        }

        public float maxDamage() {
            return maxDamage;
        }
    }

    public static final class MeteorStrike implements INBTSerializable<CompoundTag> {
        private final int defaultCdDelay;
        private int cdDelay;
        private final int defaultCd;
        private float cd;
        private boolean isCasting;
        private int skillTick;
        private static final Multimap<Attribute, AttributeModifier> METEOR_STRIKE_MODIFIER = ImmutableMultimap.of(Attributes.MOVEMENT_SPEED, new AttributeModifier(UUID.fromString("487ACCB8-0D0B-4D63-90E7-D766D436E430"), "meteor_strike_casting", -1, AttributeModifier.Operation.MULTIPLY_TOTAL));
        /*
         * 一些常量记录    帧数为60FPS/S
         * 开大前摇  0.5S  期间无法移动
         *      前摇第一次拉远到第三人称 12帧
         *      前摇第二次拉远 10帧
         * 可移动    4S
         * 开大后摇  110帧 期间无法移动
         *      后摇镜头第一次拉近 20帧
         *      后摇镜头第二次拉近到第一人称 20帧
         */

        private MeteorStrike(int cdScaling) {
            defaultCdDelay = 20 / 5 * cdScaling;
            defaultCd = cdScaling;
//            defaultCd = 1950 * cdScaling;
            cd = defaultCd;
        }

        private void cdTick(int cd) {
            if (cd > 0 && !isCasting) {
                if (cdDelay == 0) {
                    this.cd -= cd;
                    cdDelay = defaultCdDelay;
                } else {
                    cdDelay -= cd;
                }
            }
        }

        private void skillTick(LivingEntity entity, int tick) {
            if (!isCasting) {
                return;
            }

            //todo 取消视角摇晃
            //隐身通过PlayerRender实现
            //无碰撞通过mixin实现

            //这里处于前后摇期间，实体将处于静止状态，无法移动
            if (skillTick <= 127) {
                if (skillTick == 0) {
                    //前摇开始
                    entity.getAttributes().addTransientAttributeModifiers(METEOR_STRIKE_MODIFIER);
                    entity.setDeltaMovement(0, 0, 0);
                    entity.setOnGround(true);
                } else if (skillTick > 0 && skillTick < 11) {
                    //前摇期间
                    entity.setDeltaMovement(0, 0, 0);
                    entity.setOnGround(true);
                } else if (skillTick == 11) {
                    //前摇结束
                    entity.getAttributes().removeAttributeModifiers(METEOR_STRIKE_MODIFIER);
                } else if (skillTick == 90) {
                    //后摇开始
                    entity.getAttributes().addTransientAttributeModifiers(METEOR_STRIKE_MODIFIER);
                    entity.setDeltaMovement(0, 0, 0);
                    entity.setOnGround(true);
                } else if (skillTick > 90 && skillTick < 127) {
                    if (skillTick == 110) {
                        //造成伤害
                        attack(entity);
                    }

                    //后摇期间
                    entity.setDeltaMovement(0, 0, 0);
                    entity.setOnGround(true);
                } else if (skillTick == 127) {
                    //后摇结束
                    entity.getAttributes().removeAttributeModifiers(METEOR_STRIKE_MODIFIER);
                }

                skillTick++;
            } else {
                castingCompleted(entity);
            }
        }

        private void attack(LivingEntity entity) {
            Level level = entity.level();

            if (level.isClientSide) {
                return;
            }

            //获取水平半径14，垂直半径4以内的实体
            for (Entity enemy : level.getEntities(entity, entity.getBoundingBox().inflate(14, 4, 14))) {
                double distanceToSqr = enemy.position().distanceToSqr(entity.position());

                if (distanceToSqr <= 28.09) {
                    //内圈半径5，伤害30。考虑到实体体积，按照5.3为半径计算
                    enemy.hurt(ModDamageSources.owNoKnockback(level, null, entity, entity.position()), 30f);

                    //如果活着就减速
                    if (enemy.isAlive() && enemy instanceof LivingEntity livingEntity) {
                        livingEntity.addEffect(new MobEffectInstance(ModEffects.SLOWDOWN.get(), 3 * 20, 49, false, false));
                    }
                } else if (distanceToSqr <= 176.89) {
                    //外圈半径5~13，伤害由内至外为30~5，线性降低。按照5.3~13.3为半径计算
                    enemy.hurt(ModDamageSources.owNoKnockback(level, null, entity, entity.position()), (float) (30 - (distanceToSqr - 28.09) * 0.168));

                    //如果活着就减速
                    if (enemy.isAlive() && enemy instanceof LivingEntity livingEntity) {
                        livingEntity.addEffect(new MobEffectInstance(ModEffects.SLOWDOWN.get(), 3 * 20, 49, false, false));
                    }
                }
            }
        }

        /**
         * 由实体主动确认攻击
         */
        private boolean confirm() {
            if (isCasting && skillTick > 10 && skillTick < 90) {
                skillTick = 90;
                return true;
            } else {
                return false;
            }
        }

        private boolean cantJump() {
            return isCasting && (skillTick <= 10 || skillTick > 90);
        }

        public boolean tryUse(LivingEntity entity) {
            if (cd <= 0 && !isCasting) {
                cd = defaultCd;
                isCasting = true;
                entity.addEffect(new MobEffectInstance(ModEffects.METEOR_STRIKE.get(), 5 * 20, 0, false, false, false));
                return true;
            } else {
                return false;
            }
        }

        public void charge(float damage, int chargeScaling) {
            cd = Math.max(0, cd - damage * chargeScaling);
        }

        private void castingCompleted(LivingEntity entity) {
            skillTick = 0;
            isCasting = false;
        }

        private void onQuitedMode(LivingEntity entity) {
            entity.getAttributes().removeAttributeModifiers(METEOR_STRIKE_MODIFIER);
        }

        @Override
        public CompoundTag serializeNBT() {
            CompoundTag tag = new CompoundTag();
            tag.putInt("cd_delay", cdDelay);
            tag.putFloat("cd", cd);
            tag.putBoolean("is_casting", isCasting);
            return tag;
        }

        @Override
        public void deserializeNBT(CompoundTag nbt) {
            cdDelay = nbt.getInt("cd_delay");
            cd = nbt.getFloat("cd");
            isCasting = nbt.getBoolean("is_casting");
        }
    }

    public static final class UltimateEfficiency implements INBTSerializable<CompoundTag> {
        private final float max = 20;
        /**
         * 衰减延迟，当获取护盾后，延迟一定时间才开始衰减
         */
        private final int defaultAttenuationDelay;
        /**
         * 衰减速率，每一tick衰减的护盾量
         */
        private final float attenuationRate = 0.015f;
        private int attenuationDelay;
        /**
         * 最佳防守的临时血量实例
         */
        private final ExpandedHP.IHPInstance temporaryHP = new TemporaryHP(max);


        private UltimateEfficiency(int cdScaling) {
            defaultAttenuationDelay = 20 * cdScaling;
        }

        public void cdTick(int cd) {
            if (attenuationDelay > 0) {
                attenuationDelay -= cd;
            } else {
                temporaryHP.hurt(attenuationRate);
            }
        }

        public void generate(int count) {
            attenuationDelay = defaultAttenuationDelay;
            temporaryHP.heal(count * 3.5f);
        }

        @Override
        public CompoundTag serializeNBT() {
            CompoundTag tag = new CompoundTag();
            tag.putInt("attenuation_delay", attenuationDelay);
            return tag;
        }

        @Override
        public void deserializeNBT(CompoundTag nbt) {
            attenuationDelay = nbt.getInt("attenuation_delay");
        }
    }

    public static final class AuxiliaryAttack implements INBTSerializable<IntTag> {
        private final int defaultCd;
        private int cd;

        private AuxiliaryAttack(int cdScaling) {
            defaultCd = 20 * cdScaling;
        }

        private void cdTick(int tick) {
            if (cd > 0) {
                cd -= tick;
            }
        }

        /**
         * @return 是否可以进行主要攻击
         */
        private boolean canMainAttack() {
            return cd * 2 <= defaultCd;
        }

        /**
         * 尝试进行近战攻击
         *
         * @return 是否成功近战
         */
        private boolean attack(LivingEntity entity) {
            if (cd <= 0) {
                cd = defaultCd;

                findAndHurtEnemy(entity);

                return true;
            } else {
                return false;
            }
        }

        /**
         * 寻找并伤害敌人<br>
         * 利用OBB碰撞箱来精细判断
         */
        private static void findAndHurtEnemy(LivingEntity entity) {
            List<LivingEntity> entityList = entity.level().getEntitiesOfClass(LivingEntity.class, entity.getBoundingBox().inflate(3));

            OBB obb = new OBB(entity.getEyePosition().toVector3f()
                    , new Vector3f(0.5f, 0.5f, 0.75f)
                    , new Quaternionf());
            obb.rotation().rotateYXZ(-(float) Math.toRadians(entity.getYRot()), (float) Math.toRadians(entity.getXRot()), 0);
            obb.setRotationDirt();
            obb.move(0.75f);

            for (Entity enemy : entityList) {
                if (obb.isCollision(enemy)) {
                    enemy.hurt(ModDamageSources.owNoKnockback(entity.level(), null, entity, entity.position()), 3);
                }
            }
        }


        @Override
        public IntTag serializeNBT() {
            return IntTag.valueOf(cd);
        }

        @Override
        public void deserializeNBT(IntTag nbt) {
            cd = nbt.getAsInt();
        }
    }

    public static final class HandCannon implements INBTSerializable<CompoundTag> {
        private final FloatConsumer ultimateCharger;
        private final int defaultRechargeCd;
        private final int defaultShootedRechargeCd;
        private final int defaultShootCd;
        private int bulletCount;
        public int rechargeCd;
        public int shootCd;

        private HandCannon(int tickScaling, FloatConsumer ultimateCharger) {
            defaultRechargeCd = 8 * tickScaling;
            defaultShootCd = 6 * tickScaling;
            defaultShootedRechargeCd = 15 * tickScaling;
            this.ultimateCharger = ultimateCharger;
        }

        /**
         * 进行cd的计算，并尝试装填弹药
         */
        public void cdTick(LivingEntity livingEntity, int tickScaling) {
            if (shootCd > 0) {
                shootCd -= tickScaling;
            }

            if (rechargeCd > 0) {
                rechargeCd -= tickScaling;
            }

            if (rechargeCd <= 0 && bulletCount < 4) {
                bulletCount++;
                rechargeCd = defaultRechargeCd;

                if (livingEntity.level().isClientSide) {
                    livingEntity.playSound(ModSounds.HAND_CANNON_RELOAD.get(), 0.5f, 1);
                }
            }
        }

        /**
         * 尝试进行射击
         */
        public void shoot(LivingEntity entity) {
            if (bulletCount > 0 && shootCd <= 0) {
                shootCd = defaultShootCd;
                bulletCount--;
                rechargeCd = defaultShootedRechargeCd;
                shootBullet(entity);
            }
        }

        private void shootBullet(LivingEntity entity) {
            Level level = entity.level();

            if (level.isClientSide) {
                return;
            }

            level.playSound(null, entity.getX(), entity.getY(), entity.getZ(), ModSounds.HAND_CANNON_SHOOT.get(), entity.getSoundSource());
            float velocity = 3f;
            shootOneBullet(entity, velocity, 0, 0);
            shootOneBullet(entity, velocity, 2, 0);
            shootOneBullet(entity, velocity, -2, 0);
            shootOneBullet(entity, velocity, 0, 2);
            shootOneBullet(entity, velocity, 0, -2);
            shootOneBullet(entity, velocity, 1, 1);
            shootOneBullet(entity, velocity, 1, -1);
            shootOneBullet(entity, velocity, -1, 1);
            shootOneBullet(entity, velocity, -1, -1);
        }

        private void shootOneBullet(LivingEntity shooter, float velocity, float xAngle, float yAngle) {
            LightBullet bullet = new LightBullet(shooter, ultimateCharger);

            if (yAngle == 0) {
                bullet.shootFromRotation(shooter, shooter.getXRot() + xAngle, shooter.getYRot(), 0, velocity, 0);
            } else {
                Vec3 vec3 = shooter.getUpVector(1.0F);
                Quaternionf quaternionf = new Quaternionf().setAngleAxis(yAngle * (float) (Math.PI / 180.0), vec3.x, vec3.y, vec3.z);

                float f = (shooter.getXRot() + xAngle) * (float) (Math.PI / 180.0);
                float f1 = -shooter.getYRot() * (float) (Math.PI / 180.0);
                float f2 = Mth.cos(f1);
                float f3 = Mth.sin(f1);
                float f4 = Mth.cos(f);
                float f5 = Mth.sin(f);
                Vector3f view = new Vector3f(f3 * f4, -f5, f2 * f4);

                Vector3f vector3f = view.rotate(quaternionf);
                bullet.shoot(vector3f.x(), vector3f.y(), vector3f.z(), velocity, 0);
            }

            shooter.level().addFreshEntity(bullet);
        }

        @Override
        public CompoundTag serializeNBT() {
            CompoundTag tag = new CompoundTag();
            tag.putInt("bullet_count", bulletCount);
            tag.putInt("recharge_delay", rechargeCd);
            return tag;
        }

        @Override
        public void deserializeNBT(CompoundTag nbt) {
            bulletCount = nbt.getInt("bullet_count");
            rechargeCd = nbt.getInt("recharge_delay");
        }

        public int bulletCount() {
            return bulletCount;
        }
    }

    public static final class DoomfistAnim implements IAnimController {
        private DoomfistAnim() {
        }

        @Override
        public void triggerAnim(LivingEntity relatedEntity, @Nullable String controllerName, String animName) {

        }
    }

    private static final class TemporaryHP extends ExpandedHP.BaseHP {
        public TemporaryHP(float amount) {
            super(amount);
            this.amount = 0;
        }
    }
}
